package cn.jiedanba.cacert.admin.service.impl;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.ModelMap;

import cn.jiedanba.cacert.admin.service.CAService;
import cn.jiedanba.cacert.admin.vo.IssueRootCaVo;
import cn.jiedanba.cacert.admin.vo.RevokeCaVo;
import cn.jiedanba.cacert.common.ca.PkiUtil;
import cn.jiedanba.cacert.common.ca.algorithm.AlgorithmConstant;
import cn.jiedanba.cacert.common.ca.cert.PkiCertUtil;
import cn.jiedanba.cacert.common.ca.cert.enums.CertStatusEnum;
import cn.jiedanba.cacert.common.ca.crl.PkiCRLUtil;
import cn.jiedanba.cacert.common.ca.crl.domain.PkiCRL;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCert;
import cn.jiedanba.cacert.common.ca.extension.domain.PkiCertPolicy;
import cn.jiedanba.cacert.common.ca.generator.RandomSerialNumberGenerator;
import cn.jiedanba.cacert.common.ca.keypair.KeyPairUtil;
import cn.jiedanba.cacert.common.config.Configs;
import cn.jiedanba.cacert.common.result.ResponseResult;
import cn.jiedanba.cacert.common.util.DateUtil;
import cn.jiedanba.cacert.mapper.KeyUsageMapper;
import cn.jiedanba.cacert.mapper.RootCaMapper;
import cn.jiedanba.cacert.mapper.RootCrlMapper;
import cn.jiedanba.cacert.mapper.RootRevokeMapper;
import cn.jiedanba.cacert.model.KeyUsage;
import cn.jiedanba.cacert.model.RootCa;
import cn.jiedanba.cacert.model.RootCrl;
import cn.jiedanba.cacert.model.RootRevoke;
import lombok.extern.slf4j.Slf4j;

/**
 * CA管理
 * 
 * @author
 *
 */
@Service
@Slf4j
public class CAServiceImpl implements CAService {

	@Resource
	private RootCaMapper rootCaMapper;

	@Resource
	private KeyUsageMapper keyUsageMapper;

	@Resource
	private RootCrlMapper rootCrlMapper;

	@Resource
	private RootRevokeMapper rootRevokeMapper;

	/**
	 * 查询根证书
	 * 
	 * @return
	 */
	@Override
	public List<RootCa> selectRootCaList(RootCa ca) {
		List<RootCa> roots = rootCaMapper.selectRootCaList(ca);

		for (int i = 0; i < roots.size(); i++) {
			/*
			 * // 查询密钥用法 StringBuffer usageSb = new StringBuffer(); String[]
			 * usageStr = roots.get(i).getKeyUsage().split(","); for (String
			 * string : usageStr) { KeyUsage usage =
			 * keyUsageMapper.selectOneById(string); if (usage != null) {
			 * usageSb.append(usage.getUsageName() + ","); } }
			 * roots.get(i).setKeyUsage(usageSb.toString().substring(0,
			 * usageSb.toString().length() - 1)); results.add(roots.get(i));
			 */

			RootCa subQuery = new RootCa();
			subQuery.setParentId(roots.get(i).getId());
			List<RootCa> subRoots = rootCaMapper.selectSubRootCaList(subQuery);
			/*
			 * for (int j = 0; j < subRoots.size(); j++) { // 查询密钥用法
			 * StringBuffer subUsageSb = new StringBuffer(); String[] subUsage =
			 * subRoots.get(j).getKeyUsage().split(","); for (String string :
			 * subUsage) { KeyUsage usage =
			 * keyUsageMapper.selectOneById(string); if (usage != null) {
			 * subUsageSb.append(usage.getUsageName() + ","); } }
			 * subRoots.get(j).setKeyUsage(subUsageSb.toString().substring(0,
			 * subUsageSb.toString().length() - 1));
			 * 
			 * }
			 */
			roots.addAll(subRoots);

		}
		return roots;
	}

	/**
	 * 下载签名证书
	 * 
	 * @return
	 * @throws Exception
	 */
	@Override
	public ResponseResult downloadSignCert(String id) {
		try {
			RootCa root = rootCaMapper.selectOneById(id);
			X509Certificate signCert = PkiUtil.readX509Certificate(Base64.decodeBase64(root.getSignCert()));
			String pem = PkiUtil.toPemObject("CERTIFICATE", signCert.getEncoded());
			ModelMap mm = new ModelMap();
			mm.addAttribute("cert", pem.getBytes());
			mm.addAttribute("caName", root.getCertName());
			return ResponseResult.success(mm);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("证书下载异常");
		}

	}

	/**
	 * 根据id查询
	 * 
	 * @return
	 */
	@Override
	public RootCa selectRootCaById(String id) {
		return rootCaMapper.selectOneById(id);
	}

	/**
	 * 颁发根证书
	 * 
	 * @param rootCa
	 * @return
	 */
	@Transactional
	@Override
	public ResponseResult issueRootCa(IssueRootCaVo vo) {

		try {
			// 颁发下级CA证书
			if (StringUtils.isNotBlank(vo.getParentId())) {
				RootCa ca = rootCaMapper.selectOneById(vo.getParentId());
				if (ca == null) {
					return ResponseResult.fail("根证书不存在");
				}
				if (ca.getStatus().equals(CertStatusEnum.REVOKE.code)) {
					return ResponseResult.fail("根证书已被吊销");
				}

				if (ca.getNotBefore().getTime() > System.currentTimeMillis()) {
					return ResponseResult.fail("根证书已过期");
				}

				// 构建颁发证书参数
				PkiCert buildVo = new PkiCert();
				buildVo.setStartTime(DateUtil.getNTPDate(Configs.get("ntp.url")));
				buildVo.setEndTime(DateUtil.getNextDay(buildVo.getStartTime(), Integer.valueOf(vo.getYear()) * 365));
				if (buildVo.getEndTime().getTime() >= ca.getNotAfter().getTime()) {
					return ResponseResult.fail("CA有效天数不能大于根证书过期时间");
				}
				// 颁发者私钥和证书
				X509Certificate issuerCert = PkiUtil.readX509Certificate(Base64.decodeBase64(ca.getSignCert()));
				PrivateKey issuerPrivateKey = PkiUtil.getPrivateKey(Base64.decodeBase64(ca.getPrivateKey()));
				buildVo.setCA(true);
				buildVo.setIssuerCert(issuerCert);
				buildVo.setIssuerPrivateKey(issuerPrivateKey);
				buildVo.setSignatureAlgorithm(ca.getSignAlgorithm());

				buildVo.setSerialNumber(RandomSerialNumberGenerator.getInstance().nextSerialNumber(10));
				buildVo.setCrl(vo.getCrl());
				buildVo.setOscp(ca.getOcsp());
				buildVo.setCrt(ca.getCrt());

				// 根证书固定密钥用法 数字签名，CRL签名，证书签名
				buildVo.setUsage(new Integer[] { 128, 2, 4 });
				String[] extendUsages = vo.getEnhanceUsage();
				List<String> extendUsageOids = new ArrayList<String>();
				for (String string : extendUsages) {
					KeyUsage keyUsage = keyUsageMapper.selectOneById(string);
					extendUsageOids.add(keyUsage.getUsageValue());
				}
				buildVo.setExtendUsage(extendUsageOids.toArray(new String[extendUsageOids.size()]));

				List<PkiCertPolicy> policyOids = new ArrayList<PkiCertPolicy>();
				for (String string : vo.getPolicyOids()) {
					PkiCertPolicy ent = new PkiCertPolicy();
					ent.setPolicyOid(string);
					policyOids.add(ent);
				}
				buildVo.setPolicyVos(policyOids);

				KeyPair keyPair = KeyPairUtil.generateKeyPair(ca.getAlgorithm(), vo.getKeySize());

				buildVo.setSubjectDN(PkiCertUtil.buildName(vo.getSubjectDn()));
				buildVo.setSubjectPublicKey(keyPair.getPublic());

				X509Certificate signCert = PkiCertUtil.buildSubjectCert(buildVo);

				RootCa newCa = new RootCa();
				newCa.setParentId(ca.getId());
				newCa.setCertType("ROOT");
				RDN CN = buildVo.getSubjectDN().getRDNs(BCStyle.CN)[0];
				newCa.setCertName(CN.getFirst().getValue().toString());
				newCa.setSerialNumber(PkiUtil.serialNumberConvertString(buildVo.getSerialNumber()).toUpperCase());
				newCa.setSubjectDn(vo.getSubjectDn());

				newCa.setPrivateKey(Base64.encodeBase64String(keyPair.getPrivate().getEncoded()));
				newCa.setSignCert(Base64.encodeBase64String(signCert.getEncoded()));
				newCa.setSignAlgorithm(buildVo.getSignatureAlgorithm());
				newCa.setAlgorithm(ca.getAlgorithm());
				newCa.setNotBefore(buildVo.getStartTime());
				newCa.setNotAfter(buildVo.getEndTime());
				newCa.setKeyUsage("keyCertSign,cRLSign");
				newCa.setPublicKeySize(PkiUtil.getKeyLength(signCert.getPublicKey()));
				newCa.setStatus(CertStatusEnum.NORMAL.code);
				newCa.setOcsp(vo.getOcsp());
				newCa.setCrl(vo.getCrl());
				newCa.setCrt(vo.getCrt());
				newCa.setCreateDate(new Date());
				rootCaMapper.insertSelective(newCa);

				// 签发空白crl
				PkiCRL pkiCRL = new PkiCRL();
				pkiCRL.setSignCert(signCert);
				pkiCRL.setPrivateKey(keyPair.getPrivate());
				pkiCRL.setAlgorithm(newCa.getSignAlgorithm());
				pkiCRL.setCertSerial(buildVo.getSerialNumber());
				pkiCRL.setNextUpdate(DateUtil.getNextDay(new Date(), 1));
				X509CRL x509crl = PkiCRLUtil.createX509v2CRLEmpty(pkiCRL);
				RootCrl crl = new RootCrl();
				crl.setRootId(newCa.getId());
				crl.setCertName(newCa.getCertName());
				crl.setSignAlgorithm(buildVo.getSignatureAlgorithm());
				crl.setStartDate(new Date());
				crl.setNextUpdateDate(pkiCRL.getNextUpdate());
				crl.setCrlFile(Base64.encodeBase64String(x509crl.getEncoded()));
				crl.setCreateDate(new Date());
				rootCrlMapper.insertSelective(crl);

			} else {
				KeyPair keyPair = KeyPairUtil.generateKeyPair(vo.getAlgorithm(), 2048);
				String signatureAlgorithm = null;
				RootCa newCa = new RootCa();
				if (StringUtils.equalsAnyIgnoreCase(AlgorithmConstant.RSA, vo.getAlgorithm())) {
					signatureAlgorithm = AlgorithmConstant.SHA256WithRSA;
					newCa.setAlgorithm(AlgorithmConstant.RSA);
				} else if (StringUtils.equalsAnyIgnoreCase(AlgorithmConstant.SM2, vo.getAlgorithm())) {
					signatureAlgorithm = AlgorithmConstant.SM3WithSM2;
					newCa.setAlgorithm(AlgorithmConstant.SM2);
				}

				Date notBefore = DateUtil.getNTPDate(Configs.get("ntp.url"));
				Date notAfter = DateUtil.getNextDay(notBefore, Integer.valueOf(vo.getYear()) * 365);

				BigInteger serialNumber = RandomSerialNumberGenerator.getInstance().nextSerialNumber(10);
				X500Name subjectDn = PkiCertUtil.buildName(vo.getSubjectDn());
				X509Certificate signCert = PkiCertUtil.buildRootCert(subjectDn, serialNumber, notBefore, notAfter,
						keyPair, signatureAlgorithm);

				newCa.setCertType("ROOT");
				RDN CN = subjectDn.getRDNs(BCStyle.CN)[0];
				newCa.setCertName(CN.getFirst().getValue().toString());
				newCa.setSerialNumber(PkiUtil.serialNumberConvertString(serialNumber).toUpperCase());
				newCa.setSubjectDn(vo.getSubjectDn());
				newCa.setPrivateKey(Base64.encodeBase64String(keyPair.getPrivate().getEncoded()));
				newCa.setSignCert(Base64.encodeBase64String(signCert.getEncoded()));
				newCa.setSignAlgorithm(signatureAlgorithm);
				newCa.setNotBefore(notBefore);
				newCa.setNotAfter(notAfter);
				newCa.setKeyUsage("keyCertSign,cRLSign");
				newCa.setPublicKeySize(PkiUtil.getKeyLength(signCert.getPublicKey()));
				newCa.setStatus(CertStatusEnum.NORMAL.code);
				newCa.setOcsp(vo.getOcsp());
				newCa.setCrl(vo.getCrl());
				newCa.setCrt(vo.getCrt());
				newCa.setCreateDate(new Date());
				rootCaMapper.insertSelective(newCa);

				// 签发空白crl
				PkiCRL pkiCRL = new PkiCRL();
				pkiCRL.setSignCert(signCert);
				pkiCRL.setPrivateKey(keyPair.getPrivate());
				pkiCRL.setAlgorithm(newCa.getSignAlgorithm());
				pkiCRL.setCertSerial(serialNumber);
				pkiCRL.setNextUpdate(DateUtil.getNextDay(new Date(), 1));
				X509CRL x509crl = PkiCRLUtil.createX509v2CRLEmpty(pkiCRL);
				RootCrl crl = new RootCrl();
				crl.setRootId(newCa.getId());
				crl.setCertName(newCa.getCertName());
				crl.setSignAlgorithm(signatureAlgorithm);
				crl.setStartDate(new Date());
				crl.setNextUpdateDate(pkiCRL.getNextUpdate());
				crl.setCrlFile(Base64.encodeBase64String(x509crl.getEncoded()));
				crl.setCreateDate(new Date());
				rootCrlMapper.insertSelective(crl);

			}
			return ResponseResult.success("CA颁发成功");
		} catch (Exception e) {
			log.error("签发CA证书异常", e);
			throw new RuntimeException(e);
		}

	}

	/**
	 * 吊销CA证书
	 * 
	 * @param vo
	 * @return
	 */
	@Transactional
	@Override
	public ResponseResult revokeCa(RevokeCaVo vo) {
		try {

			RootCa ca = rootCaMapper.selectOneById(vo.getRootId());
			if (ca == null) {
				return ResponseResult.fail("根证书不存在");
			}
			if (ca.getStatus().equals(CertStatusEnum.REVOKE.code)) {
				return ResponseResult.fail("根证书已被吊销");
			}

			if (ca.getNotBefore().getTime() > System.currentTimeMillis()) {
				return ResponseResult.fail("根证书已过期");
			}

			RootRevoke revoke = new RootRevoke();
			revoke.setRootId(ca.getId());
			revoke.setIsCrl(false);
			revoke.setRevokeDate(DateUtil.getNTPDate(Configs.get("ntp.url")));
			revoke.setReason(Integer.valueOf(vo.getReason()));
			revoke.setSerialNumber(ca.getSerialNumber());
			revoke.setCreateDate(revoke.getRevokeDate());
			rootRevokeMapper.insertSelective(revoke);

			ca.setStatus(CertStatusEnum.REVOKE.code);
			ca.setUpdateDate(new Date());
			rootCaMapper.update(ca);
			return ResponseResult.success("CA吊销成功");
		} catch (Exception e) {
			log.error("吊销CA证书异常", e);
			throw new RuntimeException(e);
		}
	}

}
